#ifndef SIMTIME_HPP
#define SIMTIME_HPP

#include "typeAliases.hpp"
#include "signalCodes.hpp"
#include "logger.hpp"
#include "invalidByDefault.hpp"

namespace hms
{

struct TimeSettings : public InvalidByDefault {
	static constexpr scalar
		defaultWriteTimeInterval {1},
		defaultInitialStep       {1e-6};
	static constexpr long
		defaultWriteStepInterval {10};
	
	scalar
		start {0},
		end {signalCode::uninitialised},
		maxTimeStep {std::numeric_limits<scalar>::max()},
		initialStep { defaultInitialStep };

	bool   writeAtTimeIntervals {true};
	scalar writeTimeInterval {defaultWriteTimeInterval};
	long   writeStepInterval {defaultWriteStepInterval};

	bool useExplicitFirstStep {false};
	
	#ifndef NDEBUG
	long maxSteps { std::numeric_limits<long>::max() };
	#endif
};

/**
 * @brief Provides simulation time functionality.
 * 
 */
class Time
{

public:
	TimeSettings settings;

private:
	scalar m_current {0};
	long   m_nSteps {0};
	int    m_nWritten {0};
	scalar m_step {TimeSettings::defaultInitialStep};

public:
	Time() = default;

	Time( scalar end );

	Time( TimeSettings&& );

	Time(
		scalar end,
		scalar writeTimeInterval
	);

	explicit Time(
		scalar start,
		scalar end,
		scalar maxTimeStep,
		scalar writeTimeInterval
	);

	explicit Time(
		scalar start,
		scalar end,
		scalar maxTimeStep,
		long int writeStepInterval
	);

	scalar current() const;
	scalar end() const;
	scalar maxTimeStep() const;

	bool  writeAtTimeIntervals() const;
	scalar   writeTimeInterval() const;
	long int writeStepInterval() const;
	int nWritten() const;

	long int nSteps() const;
	scalar step() const;
	void step( scalar );
	
	bool atStart() const;
	bool atEnd() const;
	bool atWriteTime() const;
	bool isInitialised() const;

	void startAt( scalar );
	void endAt( scalar );
	void maxTimeStep( scalar );

	void writeAtTimeIntervals( bool );
	void writeTimeInterval( scalar );
	void writeStepInterval( long int );

	void advanceBy( scalar );
	void advance();

	void print( log::logger& );

	void useExplicitFirstStep(bool);
	bool useExplicitFirstStep() const;

#ifndef NDEBUG
// private:
// 	long m_maxSteps { std::numeric_limits<long>::max() };
public:
	enum class UseMaxSteps { dummyVal };
	explicit Time(
		UseMaxSteps dummy,
		long maxSteps
	);

	long maxSteps() const;
	void maxSteps(long);
#endif

};


}

#endif